package com.seafood.service.impl;

import com.seafood.common.PageResult;
import com.seafood.dto.TraceabilityDTO;
import com.seafood.dto.TraceabilityQueryDTO;
import com.seafood.entity.Product;
import com.seafood.entity.Traceability;
import com.seafood.repository.ProductRepository;
import com.seafood.repository.TraceabilityRepository;
import com.seafood.service.TraceabilityService;
import com.seafood.util.QRCodeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * 溯源服务实现类
 */
@Service
@RequiredArgsConstructor
@Transactional
public class TraceabilityServiceImpl implements TraceabilityService {
    
    private final TraceabilityRepository traceabilityRepository;
    private final ProductRepository productRepository;
    private final QRCodeUtil qrCodeUtil;
    
    @Override
    public TraceabilityDTO createTraceability(TraceabilityDTO traceabilityDTO) {
        // 检查产品是否存在
        Product product = productRepository.findById(traceabilityDTO.getProductId())
                .orElseThrow(() -> new RuntimeException("产品不存在"));
        
        // 生成批次号（如果没有提供）
        if (traceabilityDTO.getBatchNumber() == null || traceabilityDTO.getBatchNumber().isEmpty()) {
            traceabilityDTO.setBatchNumber(generateBatchNumber(traceabilityDTO.getProductId()));
        }
        
        // 检查批次号是否已存在
        if (traceabilityRepository.existsByBatchNumber(traceabilityDTO.getBatchNumber())) {
            throw new RuntimeException("批次号已存在");
        }
        
        Traceability traceability = convertToEntity(traceabilityDTO);
        traceability.setProduct(product);
        
        // 生成二维码内容
        String qrContent = qrCodeUtil.generateQRContent(traceability.getBatchNumber());
        traceability.setQrCode(qrContent);
        
        Traceability savedTraceability = traceabilityRepository.save(traceability);
        return convertToDTO(savedTraceability);
    }
    
    @Override
    public TraceabilityDTO updateTraceability(Long id, TraceabilityDTO traceabilityDTO) {
        Traceability traceability = traceabilityRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("溯源信息不存在"));
        
        // 更新字段
        traceability.setFishingVessel(traceabilityDTO.getFishingVessel());
        traceability.setFishingLocation(traceabilityDTO.getFishingLocation());
        traceability.setFishingCoordinates(traceabilityDTO.getFishingCoordinates());
        traceability.setFishingDate(traceabilityDTO.getFishingDate());
        traceability.setFishingMethod(traceabilityDTO.getFishingMethod());
        traceability.setProcessingFacility(traceabilityDTO.getProcessingFacility());
        traceability.setProcessingDate(traceabilityDTO.getProcessingDate());
        traceability.setFreezingMethod(traceabilityDTO.getFreezingMethod());
        traceability.setQualityCertificate(traceabilityDTO.getQualityCertificate());
        traceability.setInspectionReport(traceabilityDTO.getInspectionReport());
        traceability.setTransportCompany(traceabilityDTO.getTransportCompany());
        traceability.setTransportTemperature(traceabilityDTO.getTransportTemperature());
        
        Traceability savedTraceability = traceabilityRepository.save(traceability);
        return convertToDTO(savedTraceability);
    }
    
    @Override
    public void deleteTraceability(Long id) {
        Traceability traceability = traceabilityRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("溯源信息不存在"));
        
        traceabilityRepository.delete(traceability);
    }
    
    @Override
    @Transactional(readOnly = true)
    public TraceabilityDTO getTraceabilityByBatchNumber(String batchNumber) {
        Traceability traceability = traceabilityRepository.findByBatchNumber(batchNumber)
                .orElseThrow(() -> new RuntimeException("未找到该批次的溯源信息"));
        
        return convertToDTO(traceability);
    }
    
    @Override
    @Transactional(readOnly = true)
    public TraceabilityDTO getTraceabilityByQrCode(String qrCode) {
        Traceability traceability = traceabilityRepository.findByQrCode(qrCode)
                .orElseThrow(() -> new RuntimeException("未找到该二维码对应的溯源信息"));
        
        return convertToDTO(traceability);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<TraceabilityDTO> getTraceabilityByProductId(Long productId) {
        List<Traceability> traceabilityList = traceabilityRepository.findByProductIdOrderByCreatedAtDesc(productId);
        
        return traceabilityList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }
    
    @Override
    public String generateBatchNumber(Long productId) {
        // 生成批次号格式：产品ID + 日期 + 随机数
        // 例：P123202401201001
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String randomStr = String.format("%04d", new Random().nextInt(10000));
        String batchNumber = "P" + productId + dateStr + randomStr;
        
        // 确保批次号唯一
        while (traceabilityRepository.existsByBatchNumber(batchNumber)) {
            randomStr = String.format("%04d", new Random().nextInt(10000));
            batchNumber = "P" + productId + dateStr + randomStr;
        }
        
        return batchNumber;
    }
    
    @Override
    public byte[] generateQRCode(String batchNumber) {
        return qrCodeUtil.generateQRCodeForBatch(batchNumber);
    }
    
    @Override
    public String getQRCodeUrl(String batchNumber) {
        return qrCodeUtil.generateQRContent(batchNumber);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean existsByBatchNumber(String batchNumber) {
        return traceabilityRepository.existsByBatchNumber(batchNumber);
    }
    
    @Override
    public TraceabilityDTO convertToDTO(Traceability traceability) {
        TraceabilityDTO dto = new TraceabilityDTO();
        dto.setId(traceability.getId());
        dto.setBatchNumber(traceability.getBatchNumber());
        dto.setProductId(traceability.getProduct().getId());
        dto.setProductName(traceability.getProduct().getName());
        dto.setFishingVessel(traceability.getFishingVessel());
        dto.setFishingLocation(traceability.getFishingLocation());
        dto.setFishingCoordinates(traceability.getFishingCoordinates());
        dto.setFishingDate(traceability.getFishingDate());
        dto.setFishingMethod(traceability.getFishingMethod());
        dto.setProcessingFacility(traceability.getProcessingFacility());
        dto.setProcessingDate(traceability.getProcessingDate());
        dto.setFreezingMethod(traceability.getFreezingMethod());
        dto.setQualityCertificate(traceability.getQualityCertificate());
        dto.setInspectionReport(traceability.getInspectionReport());
        dto.setQrCode(traceability.getQrCode());
        dto.setQrCodeUrl(traceability.getQrCodeUrl());
        dto.setTransportCompany(traceability.getTransportCompany());
        dto.setTransportTemperature(traceability.getTransportTemperature());
        
        if (traceability.getCreatedAt() != null) {
            dto.setCreatedAt(traceability.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
        
        if (traceability.getUpdatedAt() != null) {
            dto.setUpdatedAt(traceability.getUpdatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
        
        return dto;
    }
    
    @Override
    public PageResult<TraceabilityDTO> queryTraceability(TraceabilityQueryDTO queryDTO) {
        // 构建查询条件
        Specification<Traceability> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            // 批次号查询
            if (queryDTO.getBatchNumber() != null && !queryDTO.getBatchNumber().isEmpty()) {
                predicates.add(criteriaBuilder.like(root.get("batchNumber"), "%" + queryDTO.getBatchNumber() + "%"));
            }
            
            // 产品名称查询（需要关联Product表）
            if (queryDTO.getProductName() != null && !queryDTO.getProductName().isEmpty()) {
                Join<Traceability, Product> productJoin = root.join("product");
                predicates.add(criteriaBuilder.like(productJoin.get("name"), "%" + queryDTO.getProductName() + "%"));
            }
            
            // 渔船查询
            if (queryDTO.getFishingVessel() != null && !queryDTO.getFishingVessel().isEmpty()) {
                predicates.add(criteriaBuilder.like(root.get("fishingVessel"), "%" + queryDTO.getFishingVessel() + "%"));
            }
            
            // 捕捞地点查询
            if (queryDTO.getFishingLocation() != null && !queryDTO.getFishingLocation().isEmpty()) {
                predicates.add(criteriaBuilder.like(root.get("fishingLocation"), "%" + queryDTO.getFishingLocation() + "%"));
            }
            
            // 捕捞日期范围查询
            if (queryDTO.getFishingDateStart() != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("fishingDate"), queryDTO.getFishingDateStart()));
            }
            
            if (queryDTO.getFishingDateEnd() != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("fishingDate"), queryDTO.getFishingDateEnd()));
            }
            
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        
        // 分页查询
        Pageable pageable = PageRequest.of(queryDTO.getPage(), queryDTO.getSize());
        Page<Traceability> page = traceabilityRepository.findAll(spec, pageable);
        
        // 转换为DTO
        List<TraceabilityDTO> dtoList = page.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return PageResult.of(dtoList, page.getTotalElements(), page.getTotalPages(), 
                page.getNumber(), page.getSize(), page.isFirst(), page.isLast());
    }
    
    @Override
    public Traceability convertToEntity(TraceabilityDTO traceabilityDTO) {
        Traceability traceability = new Traceability();
        traceability.setBatchNumber(traceabilityDTO.getBatchNumber());
        traceability.setFishingVessel(traceabilityDTO.getFishingVessel());
        traceability.setFishingLocation(traceabilityDTO.getFishingLocation());
        traceability.setFishingCoordinates(traceabilityDTO.getFishingCoordinates());
        traceability.setFishingDate(traceabilityDTO.getFishingDate());
        traceability.setFishingMethod(traceabilityDTO.getFishingMethod());
        traceability.setProcessingFacility(traceabilityDTO.getProcessingFacility());
        traceability.setProcessingDate(traceabilityDTO.getProcessingDate());
        traceability.setFreezingMethod(traceabilityDTO.getFreezingMethod());
        traceability.setQualityCertificate(traceabilityDTO.getQualityCertificate());
        traceability.setInspectionReport(traceabilityDTO.getInspectionReport());
        traceability.setTransportCompany(traceabilityDTO.getTransportCompany());
        traceability.setTransportTemperature(traceabilityDTO.getTransportTemperature());
        
        return traceability;
    }
}